using System;
using System.Collections;
using System.Messaging;
using System.Net;

using Pegasus.Log4Net.Core;
using Pegasus.Log4Net.Appender;

namespace Pegasus.Log4Net.Appender
{
    /// <summary>
    /// Queue appender implementation.
    /// </summary>
    public class QueueAppender : AppenderSkeleton
    {
        private MessageQueue messageQueue = null;
        private string messageLabel = string.Empty;
        private string serverName = string.Empty;
        private string queueName = string.Empty;
        private int messageType = 0;

        #region Properties
        /// <summary>
        /// The label for the message.
        /// </summary>
        public string MessageLabel
        {
            get
            {
                return messageLabel;
            }
            set
            {
                messageLabel = value;
            }
        }

        /// <summary>
        /// The type of the message.
        /// </summary>
        public int MessageType
        {
            get
            {
                return messageType;
            }
            set
            {
                messageType = value;
            }
        }

        /// <summary>
        /// Gets/Sets the server dns name.
        /// </summary>
        public string Server
        {
            get
            {
                return serverName;
            }
            set
            {
                serverName = value;
            }
        }

        /// <summary>
        /// Gets/Sets the queue name.
        /// </summary>
        public string Queue
        {
            get
            {
                return queueName;
            }
            set
            {
                queueName = value;
            }
        }
        #endregion

        /// <summary>
        /// Queue appender constructor.
        /// </summary>
        public QueueAppender() : base() { }

        /// <summary>
        /// Append method implementation.
        /// </summary>
        /// <param name="loggingEvent">event information</param>
        protected override void Append( LoggingEvent loggingEvent )
        {
            try
            {
                lock( this )
                {
                    Hashtable messageData = new Hashtable();
                    messageData.Add( "Namespace", loggingEvent.LoggerName );
                    messageData.Add( "ServerName", Environment.MachineName );
                    messageData.Add( "Date", loggingEvent.TimeStamp );
                    messageData.Add( "Level", loggingEvent.Level.DisplayName );
                    messageData.Add( "Thread", loggingEvent.ThreadName );
                    messageData.Add( "Class", loggingEvent.LocationInformation.ClassName );
                    messageData.Add( "Method", loggingEvent.LocationInformation.MethodName );
                    messageData.Add( "Line", loggingEvent.LocationInformation.LineNumber );
                    messageData.Add( "File", loggingEvent.LocationInformation.FileName );
                    messageData.Add( "Message", loggingEvent.RenderedMessage );
                    string exceptionMsg = string.Empty;
                    if( loggingEvent.ExceptionObject != null )
                    {
                        exceptionMsg = ExceptionHelper.DumpExceptionAll( loggingEvent.ExceptionObject );
                    }
                    messageData.Add( "Exception", exceptionMsg );

                    SendMessage( messageData );
                }
            }
            catch( Exception exception )
            {
                ErrorHandler.Error( "QueueAppender", exception, ErrorCode.WriteFailure );
            }
        }

        private void SendMessage( Hashtable messageData )
        {
            try
            {
                ConfigureQueue();

                Message message = new Message();
                message.AppSpecific = messageType;
                message.Label = messageLabel;
                message.Priority = MessagePriority.Normal;
                message.Formatter = messageQueue.Formatter;
                message.Body = messageData;

                messageQueue.Send( message );
            }
            catch( Exception exception )
            {
                ErrorHandler.Error( "QueueAppender.SendMessage", exception, ErrorCode.WriteFailure );
            }
        }

        private void ConfigureQueue()
        {
            if( messageQueue == null )
            {
                if( string.IsNullOrEmpty( serverName ) )
                {
                    throw new ArgumentException( "Unable to initialize message queue, unkown server", "serverName" );
                }

                if( string.IsNullOrEmpty( queueName ) )
                {
                    throw new ArgumentException( "Unable to initialize message queue, unkown queue", "queueName" );
                }

                IPAddress ipAddress = null;

                try
                {
                    IPHostEntry hostEntry = Dns.GetHostEntry( serverName );
                    ipAddress = hostEntry.AddressList[ 0 ];
                }
                catch( Exception exception )
                {
                    throw new ArgumentException( string.Format( "Unable to locate an IP address for server {0}", serverName ), exception );
                }

                messageQueue = new MessageQueue( string.Format( @"FORMATNAME:DIRECT=TCP:{0}\{1}", ipAddress.ToString(), queueName ) );
                messageQueue.Formatter = new BinaryMessageFormatter();
                messageQueue.DefaultPropertiesToSend.TimeToReachQueue = TimeSpan.FromHours( 1.0 );
                messageQueue.DefaultPropertiesToSend.TimeToBeReceived = TimeSpan.FromDays( 10.0 );
            }
        }
    }
}
